package model;

import java.io.IOException;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.SourceDataLine;

/**
 * Die Klasse PlayingThread dient nur dazu dem Ihm gegebenen AudioStreams im
 * Hintergrund abzuspielen. Er muss im Play-Event erzeugt werden und beim
 * Stoppen wieder beendet werden. für die fortsetzung des Titels muss ein Neuer
 * Thread gestartet werden.
 * 
 * @author Patrick
 * @since 30.11.2011
 */
public class PlayingThread extends Thread {

    private SourceDataLine line;
    private AudioInputStream din;
    private int nBytesRead;
    private Integer nBytesWritten;
    private static byte[] data = new byte[4096];
    private volatile boolean isStoped = false;

    /**
     * Erzeugt anhand der übergebenen Werte einen neuen Thread.
     * 
     * @author Patrick
     * @since 30.11.2011
     * @param line ist der Stream in den die decodierten Daten geschrieben werden
     * @param din enthält den mp3 Stream und kann Ihn decodiert auslesen
     * @param nBytesWritten: enthält die bereits geschriebenen Bytes um festzustelen<br>
     * ob der Stream fortgesetzt wird oder von anfang an abgespielt werden soll
     */
    public PlayingThread(final SourceDataLine line, AudioInputStream din,
            Integer nBytesWritten) {
        this.isStoped = false;
        this.din = din;
        this.line = line;
        this.nBytesWritten = (nBytesWritten > 0) ? nBytesWritten - data.length
                : nBytesWritten;
    }

    @Override
    /**
     * Startet den Thread und spielt den Titel ab. ERliest die Daten in einer <br>
     * Schleife aus und schreibt diese Sofort wieder in die Ausgabe.
     * @author Patrick
     * @since 30.11.2011
     *
     * @see java.lang.Thread#run()
     */
    public void run() {
        try {
            if (this.nBytesWritten != 0)
                this.din.skip((this.nBytesWritten / 8));

            line.start();
            while (nBytesRead != -1 && !this.isStoped) {
                if (line.available() > 0) {
                    nBytesRead = din.read(data, 0, data.length);
                    if (nBytesRead != -1)
                        nBytesWritten += line.write(data, 0, nBytesRead);
                }
            }
            this.finish();
        } catch (IOException e) {
            System.out.println("keine Audioinformationen gefunden");
        }
    }

    /**
     * Mit dieser Methode ist es möglich den Thread von außen anzuhalten <br>
     * und Ihn zu beenden. Um den Track zu Pausieren oder zu Stoppen. Beim Stoppen<br>
     * muss zusätzlich die Verbindung der Streams geschlossen werden.
     * 
     * @author Patrick
     * @since 30.11.2011
     * 
     * @return gibt die anzahl der geschriebenen Bytes zurück
     */
    public Integer stopped() {
        this.isStoped = true;
        if (line != null)
            this.line.stop();
        else
            System.out.println("keine Audioinformationen gefunden");
        return this.nBytesWritten;
    }

    /**
     * Beendet die Streams und schließt alle verbindungen.<br>
     * Am ende von Run wenn der Titel bis zum ende Durchgelaufen ist.
     * 
     * @author Patrick
     * @since 30.11.2011
     * 
     * @throws IOException
     */
    private void finish() throws IOException {
        this.isStoped = true;
        if (line != null || din != null) {
            line.stop();
            line.close();
            din.close();
        } else {
            System.out.println("keine Audioinformationen gefunden");
        }
    }
}
